﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Algorithm;
using YJKGridTopologyCal.Algorithm.Locate;
using YJKGridTopologyCal.Geometries;
using YJKGridTopologyCal.GeometriesGraph.Index;
using YJKGridTopologyCal.Utilities;

namespace YJKGridTopologyCal.GeometriesGraph
{
	public class GeometryGraph : PlanarGraph
	{
		public static Location DetermineBoundary(IBoundaryNodeRule boundaryNodeRule, int boundaryCount)
		{
			if (!boundaryNodeRule.IsInBoundary(boundaryCount))
			{
				return Location.Interior;
			}
			return Location.Boundary;
		}

		private static EdgeSetIntersector CreateEdgeSetIntersector()
		{
			return new SimpleMCSweepLineIntersector();
		}

		public GeometryGraph(int argIndex, IGeometry parentGeom) : this(argIndex, parentGeom, BoundaryNodeRules.OgcSfsBoundaryRule)
		{
		}

		public GeometryGraph(int argIndex, IGeometry parentGeom, IBoundaryNodeRule boundaryNodeRule)
		{
			this._argIndex = argIndex;
			this._boundaryNodeRule = boundaryNodeRule;
			this._parentGeom = parentGeom;
			if (parentGeom != null)
			{
				this.Add(parentGeom);
			}
		}

		public bool HasTooFewPoints
		{
			get
			{
				return this._hasTooFewPoints;
			}
		}

		public Coordinate InvalidPoint
		{
			get
			{
				return this._invalidPoint;
			}
		}

		public IGeometry Geometry
		{
			get
			{
				return this._parentGeom;
			}
		}

		public IBoundaryNodeRule BoundaryNodeRule
		{
			get
			{
				return this._boundaryNodeRule;
			}
		}

		public IList<Node> BoundaryNodes
		{
			get
			{
				if (this._boundaryNodes == null)
				{
					this._boundaryNodes = base.NodeMap.GetBoundaryNodes(this._argIndex);
				}
				return this._boundaryNodes;
			}
		}

		public Coordinate[] GetBoundaryPoints()
		{
			IList<Node> boundaryNodes = this.BoundaryNodes;
			Coordinate[] array = new Coordinate[boundaryNodes.Count];
			int num = 0;
			foreach (Node node in boundaryNodes)
			{
				array[num++] = (Coordinate)node.Coordinate.Clone();
			}
			return array;
		}

		public Edge FindEdge(ILineString line)
		{
			return this._lineEdgeMap[line];
		}

		public void ComputeSplitEdges(IList<Edge> edgelist)
		{
			foreach (Edge edge in base.Edges)
			{
				edge.EdgeIntersectionList.AddSplitEdges(edgelist);
			}
		}

		private void Add(IGeometry g)
		{
			if (g.IsEmpty)
			{
				return;
			}
			if (g is IMultiPolygon)
			{
				this._useBoundaryDeterminationRule = false;
			}
			if (g is IPolygon)
			{
				this.AddPolygon((IPolygon)g);
				return;
			}
			if (g is ILineString)
			{
				this.AddLineString((ILineString)g);
				return;
			}
			if (g is IPoint)
			{
				this.AddPoint((IPoint)g);
				return;
			}
			if (g is IMultiPoint)
			{
				this.AddCollection((IMultiPoint)g);
				return;
			}
			if (g is IMultiLineString)
			{
				this.AddCollection((IMultiLineString)g);
				return;
			}
			if (g is IMultiPolygon)
			{
				this.AddCollection((IMultiPolygon)g);
				return;
			}
			if (g is IGeometryCollection)
			{
				this.AddCollection((IGeometryCollection)g);
				return;
			}
			throw new NotSupportedException(g.GetType().FullName);
		}

		private void AddCollection(IGeometryCollection gc)
		{
			for (int i = 0; i < gc.NumGeometries; i++)
			{
				IGeometry geometryN = gc.GetGeometryN(i);
				this.Add(geometryN);
			}
		}

		private void AddPoint(IPoint p)
		{
			Coordinate coordinate = p.Coordinate;
			this.InsertPoint(this._argIndex, coordinate, Location.Interior);
		}

		private void AddPolygonRing(ILinearRing lr, Location cwLeft, Location cwRight)
		{
			if (lr.IsEmpty)
			{
				return;
			}
			Coordinate[] array = CoordinateArrays.RemoveRepeatedPoints(lr.Coordinates);
			if (array.Length < 4)
			{
				this._hasTooFewPoints = true;
				this._invalidPoint = array[0];
				return;
			}
			Location leftLoc = cwLeft;
			Location rightLoc = cwRight;
			if (CGAlgorithms.IsCCW(array))
			{
				leftLoc = cwRight;
				rightLoc = cwLeft;
			}
			Edge edge = new Edge(array, new Label(this._argIndex, Location.Boundary, leftLoc, rightLoc));
			this._lineEdgeMap[lr] = edge;
			base.InsertEdge(edge);
			this.InsertPoint(this._argIndex, array[0], Location.Boundary);
		}

		private void AddPolygon(IPolygon p)
		{
			this.AddPolygonRing(p.Shell, Location.Exterior, Location.Interior);
			for (int i = 0; i < p.NumInteriorRings; i++)
			{
				ILinearRing lr = p.Holes[i];
				this.AddPolygonRing(lr, Location.Interior, Location.Exterior);
			}
		}

		private void AddLineString(ILineString line)
		{
			Coordinate[] array = CoordinateArrays.RemoveRepeatedPoints(line.Coordinates);
			if (array.Length < 2)
			{
				this._hasTooFewPoints = true;
				this._invalidPoint = array[0];
				return;
			}
			Edge edge = new Edge(array, new Label(this._argIndex, Location.Interior));
			this._lineEdgeMap[line] = edge;
			base.InsertEdge(edge);
			Assert.IsTrue(array.Length >= 2, "found LineString with single point");
			this.InsertBoundaryPoint(this._argIndex, array[0]);
			this.InsertBoundaryPoint(this._argIndex, array[array.Length - 1]);
		}

		public void AddEdge(Edge e)
		{
			base.InsertEdge(e);
			Coordinate[] coordinates = e.Coordinates;
			this.InsertPoint(this._argIndex, coordinates[0], Location.Boundary);
			this.InsertPoint(this._argIndex, coordinates[coordinates.Length - 1], Location.Boundary);
		}

		public void AddPoint(Coordinate pt)
		{
			this.InsertPoint(this._argIndex, pt, Location.Interior);
		}

		public SegmentIntersector ComputeSelfNodes(LineIntersector li, bool computeRingSelfNodes)
		{
			SegmentIntersector segmentIntersector = new SegmentIntersector(li, true, false);
			EdgeSetIntersector edgeSetIntersector = GeometryGraph.CreateEdgeSetIntersector();
			if (!computeRingSelfNodes && (this._parentGeom is ILinearRing || this._parentGeom is IPolygon || this._parentGeom is IMultiPolygon))
			{
				edgeSetIntersector.ComputeIntersections(base.Edges, segmentIntersector, false);
			}
			else
			{
				edgeSetIntersector.ComputeIntersections(base.Edges, segmentIntersector, true);
			}
			this.AddSelfIntersectionNodes(this._argIndex);
			return segmentIntersector;
		}

		public SegmentIntersector ComputeEdgeIntersections(GeometryGraph g, LineIntersector li, bool includeProper)
		{
			SegmentIntersector segmentIntersector = new SegmentIntersector(li, includeProper, true);
			segmentIntersector.SetBoundaryNodes(this.BoundaryNodes, g.BoundaryNodes);
			GeometryGraph.CreateEdgeSetIntersector().ComputeIntersections(base.Edges, g.Edges, segmentIntersector);
			return segmentIntersector;
		}

		private void InsertPoint(int argIndex, Coordinate coord, Location onLocation)
		{
			Node node = base.NodeMap.AddNode(coord);
			Label label = node.Label;
			if (label == null)
			{
				node.Label = new Label(argIndex, onLocation);
				return;
			}
			label.SetLocation(argIndex, onLocation);
		}

		private void InsertBoundaryPoint(int argIndex, Coordinate coord)
		{
			Label label = base.NodeMap.AddNode(coord).Label;
			int num = 1;
			if (label.GetLocation(argIndex, Positions.On) == Location.Boundary)
			{
				num++;
			}
			Location location = GeometryGraph.DetermineBoundary(this._boundaryNodeRule, num);
			label.SetLocation(argIndex, location);
		}

		private void AddSelfIntersectionNodes(int argIndex)
		{
			foreach (Edge edge in base.Edges)
			{
				Location location = edge.Label.GetLocation(argIndex);
				foreach (EdgeIntersection edgeIntersection in edge.EdgeIntersectionList)
				{
					this.AddSelfIntersectionNode(argIndex, edgeIntersection.Coordinate, location);
				}
			}
		}

		private void AddSelfIntersectionNode(int argIndex, Coordinate coord, Location loc)
		{
			if (base.IsBoundaryNode(argIndex, coord))
			{
				return;
			}
			if (loc == Location.Boundary && this._useBoundaryDeterminationRule)
			{
				this.InsertBoundaryPoint(argIndex, coord);
				return;
			}
			this.InsertPoint(argIndex, coord, loc);
		}

		public Location Locate(Coordinate pt)
		{
			if (this._parentGeom is IPolygonal && this._parentGeom.NumGeometries > 50)
			{
				if (this._areaPtLocator == null)
				{
					this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
				}
				return this._areaPtLocator.Locate(pt);
			}
			return this._ptLocator.Locate(pt, this._parentGeom);
		}

		private readonly IGeometry _parentGeom;

		private readonly IDictionary<ILineString, Edge> _lineEdgeMap = new Dictionary<ILineString, Edge>();

		private readonly IBoundaryNodeRule _boundaryNodeRule;

		private bool _useBoundaryDeterminationRule = true;

		private readonly int _argIndex;

		private IList<Node> _boundaryNodes;

		private bool _hasTooFewPoints;

		private Coordinate _invalidPoint;

		private IPointOnGeometryLocator _areaPtLocator;

		private readonly PointLocator _ptLocator = new PointLocator();
	}
}
